{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### LLM模型包装器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_community/llms/openai.py:249: UserWarning: You are trying to use a chat model. This way of initializing it is no longer supported. Instead, please use: `from langchain_community.chat_models import ChatOpenAI`\n",
      "  warnings.warn(\n",
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_community/llms/openai.py:1070: UserWarning: You are trying to use a chat model. This way of initializing it is no longer supported. Instead, please use: `from langchain_community.chat_models import ChatOpenAI`\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "# 创建一个OpenAI LLM包装器\n",
    "from langchain.llms import OpenAI\n",
    "import os\n",
    "\n",
    "\n",
    "llm = OpenAI(model_name=\"gpt-3.5-turbo\",temperature=0.3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'Why did the scarecrow win an award? Because he was outstanding in his field!'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 让LLM包装器生成一个笑话\n",
    "llm(\"Tell me a joke\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 聊天模型包装器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.schema import (\n",
    "    AIMessage,\n",
    "    HumanMessage,\n",
    "    SystemMessage\n",
    ")\n",
    "from langchain.chat_models import ChatOpenAI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.\n",
      "  warn_deprecated(\n",
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.7 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当然，以下是一些建议的公司名称，希望能够帮助你找到合适的：\n",
      "\n",
      "1. AIgenius\n",
      "2. BrainwaveAI\n",
      "3. IntelliTech\n",
      "4. SmartMindAI\n",
      "5. ThinkBot\n",
      "6. AI Innovate\n",
      "7. NeuralNet Solutions\n",
      "8. MindMergeAI\n",
      "9. TechBrainAI\n",
      "10. AI Fusion\n",
      "\n",
      "希望这些建议对你有所帮助，如果需要更多的灵感或者有其他要求，请随时告诉我。\n"
     ]
    }
   ],
   "source": [
    "# 加载本地模型\n",
    "chat = ChatOpenAI(\n",
    "    model_name=\"gpt-3.5-turbo\",\n",
    "    temperature=0.3\n",
    ")\n",
    "\n",
    "# 创建系统和用户消息类\n",
    "messages = [\n",
    "    # 使用SystemMessage来定义LLM的背景与身份\n",
    "    SystemMessage(content=\"你是个取名大师，你擅长为创业公司取名\"),\n",
    "    HumanMessage(content=\"帮我给我的公司取个名字吧，要包含AI\"),\n",
    "]\n",
    "\n",
    "# 生成响应\n",
    "response = chat(messages)\n",
    "\n",
    "# 打印响应\n",
    "print(response.content,end=\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 提示词模板"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'You are given the below API Documentation:\\n\\nUsing this documentation, generate the full API url to call for answering the user question.\\nYou should build the API url in order to get a response that is as short as possible, while still getting the necessary information to answer the question. Pay attention to deliberately exclude any unnecessary pieces of data in the API call.\\n\\nQuestion:\\nAPI url: \\n\\nHere is the response from the API:\\n\\n\\n\\nSummarize this response to answer the original question.\\n\\nSummary:'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 导入提示词模板\n",
    "from langchain.chains.api.prompt import API_RESPONSE_PROMPT\n",
    "# 定义提示词模板\n",
    "prompt = API_RESPONSE_PROMPT.format(\n",
    "    api_docs = \"\",\n",
    "    question = \"\",\n",
    "    api_url = \"\",\n",
    "    api_response = \"\"\n",
    ")\n",
    "prompt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### PromptTemplate包装器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "You are expect data scientist with an expertise in building deep learning models.Explain the concept of NLP in a couple of lines\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from langchain import PromptTemplate\n",
    "\n",
    "# 创建模板字符串\n",
    "template = \"\"\"\n",
    "You are expect data scientist with an expertise in building deep learning models.Explain the concept of {concept} in a couple of lines\n",
    "\"\"\"\n",
    "\n",
    "# 实例化模板的第一种方式\n",
    "prompt = PromptTemplate(template=template,input_variables=['concept'])\n",
    "# 实例化的第二种方式\n",
    "prompt = PromptTemplate.from_template(template)\n",
    "# 将用户输出通过format函数传递进去\n",
    "final_prompt = prompt.format(concept=\"NLP\")\n",
    "\n",
    "# 打印最终的提示词\n",
    "print(final_prompt) # PromptTemplate返回的是字符串"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ChatPromptTemplate包装器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import (\n",
    "    ChatPromptTemplate,\n",
    "    PromptTemplate,\n",
    "    SystemMessagePromptTemplate,\n",
    "    AIMessagePromptTemplate,\n",
    "    HumanMessagePromptTemplate\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建新的模板\n",
    "template = \"\"\"\n",
    "You are an expert data scientist with an expertise in building depp learning models.\n",
    "\"\"\"\n",
    "# 创建新的系统消息模板\n",
    "system_message_prompt = SystemMessagePromptTemplate.from_template(template)\n",
    "\n",
    "# 创建人类语言模板\n",
    "human_template = \"Explain the concept of {concept} in a couple of lines\"\n",
    "human_message_prompt = \\\n",
    "HumanMessagePromptTemplate.from_template(human_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptTemplate(input_variables=['concept'], messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='\\nYou are an expert data scientist with an expertise in building depp learning models.\\n')), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['concept'], template='Explain the concept of {concept} in a couple of lines'))])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# MessageTemplate作为列表传入到ChatPromptTemplate中\n",
    "chat_prompt = ChatPromptTemplate.from_messages(\n",
    "    [system_message_prompt, human_message_prompt]\n",
    ")\n",
    "chat_prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptValue(messages=[SystemMessage(content='\\nYou are an expert data scientist with an expertise in building depp learning models.\\n'), HumanMessage(content='Explain the concept of NLP in a couple of lines')])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将用户输出通过format函数传递进去\n",
    "chat_prompt.format_prompt(concept=\"NLP\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[SystemMessage(content='\\nYou are an expert data scientist with an expertise in building depp learning models.\\n'),\n",
       " HumanMessage(content='Explain the concept of NLP in a couple of lines')]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用to_message()方法将模板转换为消息列表\n",
    "chat_prompt.format_prompt(concept=\"NLP\").to_messages()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'System: \\nYou are an expert data scientist with an expertise in building depp learning models.\\n\\nHuman: Explain the concept of NLP in a couple of lines'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用to_string()将模板消息列表内的消息转换为字符串\n",
    "chat_prompt.format_prompt(concept=\"NLP\").to_string() # 可读性更低"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 少样本提示词模板"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入FewShotPromptTemplate类\n",
    "from langchain import FewShotPromptTemplate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个样式列表\n",
    "examples = [\n",
    "    # 以字典形式来匹配槽内的变量\n",
    "    {\"input\":\"高\",\"output\":\"矮\"},\n",
    "    {\"input\":\"胖\",\"output\":\"瘦\"},\n",
    "    {\"input\":\"精力充沛\",\"output\":\"萎靡不振\"},\n",
    "    {\"input\":\"快乐\",\"output\":\"伤心\"},\n",
    "    {\"input\":\"黑\",\"output\":\"白\"},\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个提示词模板\n",
    "example_prompt = PromptTemplate(\n",
    "    input_variables=[\"input\",\"output\"],\n",
    "    template = \"\"\"\n",
    "词语：{input}\\n\n",
    "反义词：{output}\\n\n",
    "\"\"\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\n词语：高\\n\\n反义词：矮\\n\\n'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example_prompt.format(**examples[0]) # 使用format函数传递进去，使用**以kwargs自动解析"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'来玩个反义词接龙游戏，我说词语，你说它的反义词\\n\\n\\n词语：高\\n\\n反义词：矮\\n\\n\\n\\n词语：胖\\n\\n反义词：瘦\\n\\n\\n\\n词语：精力充沛\\n\\n反义词：萎靡不振\\n\\n\\n\\n词语：快乐\\n\\n反义词：伤心\\n\\n\\n\\n词语：黑\\n\\n反义词：白\\n\\n\\n现在轮到你了，词语：好\\n反义词：'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 实例化FewShotPromptTemplate类\n",
    "few_shot_template = FewShotPromptTemplate(\n",
    "    examples=examples, # 创建一个由模板变量组成的列表以供给FewShotPromptTemplate()类下的example_selector()进行枚举或随机选择\n",
    "    example_prompt=example_prompt, # 传入PromptTemplate类的实例\n",
    "    example_separator=\"\\n\", # 选择器，可以是任意字符\n",
    "    prefix=\"来玩个反义词接龙游戏，我说词语，你说它的反义词\\n\", # \n",
    "    suffix=\"现在轮到你了，词语：{input}\\n反义词：\",\n",
    "    input_variables=[\"input\"]\n",
    ")\n",
    "few_shot_template.format(input=\"好\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.llms.openai.OpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import OpenAI`.\n",
      "  warn_deprecated(\n",
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `run` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'热'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将微样本模板添加到LLM中\n",
    "from langchain.chains import LLMChain\n",
    "from langchain.llms import OpenAI\n",
    "# 创建LLM链\n",
    "chain = LLMChain(\n",
    "    llm = OpenAI(api_key=os.environ['OPENAI_API_KEY']),\n",
    "    prompt=few_shot_template\n",
    ")\n",
    "chain.run(\"冷\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了解决在超大语料中的文本选择，LangChain在`FewShotPromptTemplate`类上的设计了示例选择器（`Example Selector`）参数。示例选择器的作用是在传递给模型的示例中进行选择，以确保示例的数量和内容长度不会超过模型的处理能力。  \n",
    "示例选择器的选取策略有以下几种：\n",
    "- 示例长度\n",
    "- 输入\n",
    "- 示例之间的`N-Gram`重叠度\n",
    "来评估其相似度并打分，找到与输入具有最大余弦相似度的示例，或者通过多样性等因素来选择示例，从而保持提示成本的相对稳定。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### N-Gram"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*以下内容引用自[该知乎](https://zhuanlan.zhihu.com/p/636431618?utm_id=0)*：\n",
    "> `N-Gram`是一种基于动态滑窗的算法：将输入文本划分成N个窗口，每一个片段称之为一个`Gram`。对每个`Gram`进行词频统计，并且按照默认阈值进行过滤，形成一个`N-Gram`列表，也就是这个文本的`N-Gram`特征，列表中的每一个`Gram`就是一个特征向量维度。\n",
    "\n",
    "> 该模型基于这样一种假设，第N个词的出现只与前面N-1个词相关，而与其它任何词都不相关，整句的概率就是各个词出现概率的乘积。这些概率可以通过直接从语料中统计N个词同时出现的次数得到。\n",
    "\n",
    "- 概率计算公式\n",
    "\n",
    "$$\n",
    "P(w_1...w_n) = \\prod p(w_i|w_{i-1}...w_{1}) \\approx \\prod p(w_i|w_{i-1}...w_{i-N+1})\n",
    "$$\n",
    "\n",
    "- BiGram，即w=2（一个词的出现依赖于它前面出现的一个词）\n",
    "\n",
    "$$\n",
    "P(S) = P(w_1w_2...w_n) = p(w_1)p(w_2|w_1) ... p(w_n|w_{n-1})\n",
    "$$\n",
    "\n",
    "- TriGram，即w=3（一个词的出现依赖于它前面出现的两个词）\n",
    "\n",
    "$$\n",
    "P(S) = P(w_1w_2...w_n) = p(w_1)p(w_2|w_1) ... p(w_n|w_{n-1}w_{n-2})\n",
    "$$\n",
    "\n",
    "用极大似然估计计算每一项的频数则为：\n",
    "$$\n",
    "OneGram: p(w_n|w_{n-1}) = \\frac{C(w_{n-1}w_n)}{C(w_{n-1})}\n",
    "$$\n",
    "$$\n",
    "BiGram: P(w_n|w_{n-1}w_{n-2}) = \\frac{C(w_{n-2}w_{n-1}w_n)}{C(w_{n-2}w_{n-1})}\n",
    "$$\n",
    "$$\n",
    "TriGram: P(w_n|w_{n-1}...w_2w_1) = \\frac{C(w_1w_2...w_n)}{C(w_1w_2...w_{n-1})}\n",
    "$$\n",
    "\n",
    "*举个例子*：\n",
    "```\n",
    "我爱LLM\n",
    "我爱NLP\n",
    "我爱AI\n",
    "```\n",
    "\n",
    "那么，会得到：\n",
    "$$\n",
    "p(爱|我) = \\frac{C(我爱)}{C(我)} = \\frac{3}{3} = 1\n",
    "$$\n",
    "\n",
    "$$\n",
    "p(我|LLM) = \\frac{C(LLM我)}{C(LLM)} = \\frac{3}{1} = 3\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个长度样本选择器\n",
    "from langchain.prompts.example_selector import LengthBasedExampleSelector\n",
    "\n",
    "example_selector = LengthBasedExampleSelector(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    max_length=25\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 接着重新初始化FewShotPromptTemplate类\n",
    "example_selector_prompt = FewShotPromptTemplate(\n",
    "    example_prompt=example_prompt,\n",
    "    example_selector=example_selector,\n",
    "    example_separator=\"\\n\",\n",
    "    prefix=\"来玩个反义词接龙游戏，我说词语，你说它的反义词\\n\",\n",
    "    suffix=\"现在轮到你了，词语：{input}\\n反义词：\",\n",
    "    input_variables=[\"input\"]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'来玩个反义词接龙游戏，我说词语，你说它的反义词\\n\\n\\n词语：高\\n\\n反义词：矮\\n\\n\\n\\n词语：胖\\n\\n反义词：瘦\\n\\n\\n\\n词语：精力充沛\\n\\n反义词：萎靡不振\\n\\n\\n\\n词语：快乐\\n\\n反义词：伤心\\n\\n\\n现在轮到你了，词语：好\\n反义词：'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用format获得结果\n",
    "example_selector_prompt.format(input=\"好\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'来玩个反义词接龙游戏，我说词语，你说它的反义词\\n\\n\\n词语：高\\n\\n反义词：矮\\n\\n\\n\\n词语：胖\\n\\n反义词：瘦\\n\\n\\n\\n词语：精力充沛\\n\\n反义词：萎靡不振\\n\\n\\n\\n词语：快乐\\n\\n反义词：伤心\\n\\n\\n\\n词语：黑\\n\\n反义词：白\\n\\n\\n现在轮到你了，词语：好\\n反义词：'"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 可以看到坏还没出来就被过滤掉了，因此我们需要修改max_length参数\n",
    "example_selector = LengthBasedExampleSelector(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    max_length=100 \n",
    ")\n",
    "# 重新构建FewShotPromptTemplate类\n",
    "example_selector_prompt = FewShotPromptTemplate(\n",
    "    example_prompt=example_prompt,\n",
    "    example_selector=example_selector,\n",
    "    example_separator=\"\\n\",\n",
    "    prefix=\"来玩个反义词接龙游戏，我说词语，你说它的反义词\\n\",\n",
    "    suffix=\"现在轮到你了，词语：{input}\\n反义词：\",\n",
    "    input_variables=[\"input\"]\n",
    ")\n",
    "# 使用format获得结果\n",
    "example_selector_prompt.format(input=\"好\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 多功能提示词模板"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于不仅仅使用需要进行词填充，而更多的是追求规范化与组合化的开发时，LangChain提供了以下功能：\n",
    "- Partial提示词模板：Partial提示词模板会先传递当前的时间戳，最后剩余的部分才是用户的输入填充。在实例化的时候我们可以以指定属性值的方式来传递模板内的变量。例如：\n",
    "```python\n",
    "(partial_variables = {'foo':\"foo\"})\n",
    "```\n",
    "或者得到一个实例化对象后调用`partial()`方法：\n",
    "```python\n",
    "prompy = PromptTemplate(\n",
    "    template = \"{foo} {bar}\",\n",
    "    input_variables = [\"foo\",\"bar\"]\n",
    ")\n",
    "partial_prompt = prompt.partial(foo=\"foo\") # 调用实力化对象的partial方法\n",
    "print(partial_prompt.format(bar=\"baz\"))\n",
    "```\n",
    "此时`partial_prompt`的最终打印结果应该是：`foobaz`\n",
    "- PipelinePromptTemplate：将所有预制的模板和变量组合在一起，以便在实例化时传递给模型。例如：\n",
    "```python\n",
    "full_template = \"\"\"\n",
    "{introduction}\n",
    "{example}\n",
    "{start}\n",
    "\"\"\"\n",
    "full_prompt = PromptTemplate.from_template(full_template)\n",
    "input_prompt = [\n",
    "    {\"introduction\", introduction_prompt},\n",
    "    {\"example\", example_prompt},\n",
    "    {\"start\", start_prompt}\n",
    "]\n",
    "pipeline_prompt = PipelinePromptTemplate(final_prompt = full_prompt, pipeline_prompts = input_prompt)\n",
    "```\n",
    "- 序列化JSON和Yaml：LangChain支持将编写好的JSON或yaml格式的提示词模板进行序列化，例如：\n",
    "```json\n",
    "{\n",
    "    \"_type\": \"few_shot\",\n",
    "    \"input_variables\": [\"adjective\"],\n",
    "    \"prefix\": \"Write antonyms for the following words.\",\n",
    "    \"example_prompt\": {\n",
    "        \"_type\": \"chat\",\n",
    "        \"input_variables\": [\"input\",\"output\"],\n",
    "        \"template\": \"Input: {input}\\nOutput: {output}\"\n",
    "    },\n",
    "    \"examples\": \"examples.json\",\n",
    "    \"suffix\":\"Input: {adjective}\\nOutput:\"\n",
    "}\n",
    "```\n",
    "当我们使用`load_prompt()`函数加载该Json文件时，打印的模板信息显示如下：\n",
    "```python\n",
    "prompt = load_prompt(\"prompt.json\")\n",
    "prompt.format(adjective=\"happy\")\n",
    "```\n",
    "\n",
    "```text\n",
    "Write antonyms for the following words.\n",
    "\n",
    "    Input: happy\n",
    "    Output: sad\n",
    "\n",
    "    Input: tall\n",
    "    Output: short\n",
    "\n",
    "    Input: big\n",
    "Output:\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 输出解析器(OutputParser)\n",
    "LangChain支持以下几种输出解析器：\n",
    "- BooleanOutputParser：用于解析二元输出，例如：`True`或`False`。\n",
    "- CommaSepratedOutputParser：用于解析以逗号分隔列表类型的输出\n",
    "- DatetimeOutputParser：用于解析枚举类型的输出\n",
    "- EnumOutputParser：用于解析枚举类型的输出\n",
    "- ListOutputParset：用于解析列表类型的输出\n",
    "- PydanticOutputParser：用于解析符合Pydantic设计规范的输出，例如：可以使用BaseModel进行解构的数据结构\n",
    "- StructuredOutputParser：用于解析具有特定结构的输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 示例：使用CommaSeparatedOutputParser类实现输出解析\n",
    "from langchain.output_parsers import CommaSeparatedListOutputParser\n",
    "from langchain.prompts import PromptTemplate\n",
    "from langchain.llms import OpenAI\n",
    "output_parser = CommaSeparatedListOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "format_instructions = output_parser.get_format_instructions() # 从输出解析器中获取格式化的指令\n",
    "prompt = PromptTemplate( # 创建提示词模板\n",
    "    template=\"List five {subject}.\\n{formate_instrctions}\",\n",
    "    input_variables=['subject'],\n",
    "    partial_variables={'formate_instrctions':format_instructions} # 将通过partial()函数进行注入的部分\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建LLMChain\n",
    "from langchain.chains import LLMChain\n",
    "\n",
    "chain = LLMChain(\n",
    "    llm = OpenAI(\n",
    "        api_key=os.environ['OPENAI_API_KEY']\n",
    "    ),\n",
    "    prompt=prompt,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/elin/anaconda3/envs/langchain/lib/python3.11/site-packages/langchain_core/_api/deprecation.py:117: LangChainDeprecationWarning: The function `__call__` was deprecated in LangChain 0.1.0 and will be removed in 0.2.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "['1. Chocolate\\n2. Vanilla\\n3. Strawberry\\n4. Mint chocolate chip\\n5. Cookies and cream']"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output = chain(\"ice cream flavors\")\n",
    "output_parser.parse(output['text']) # 使用输出解析器解析输出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基于Pydantic的JSON输出解析器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入提示词模板和LLMs\n",
    "from langchain.prompts import PromptTemplate\n",
    "from langchain.llms import OpenAI\n",
    "# 导入Pydantic和Pydantic解析器\n",
    "from langchain.output_parsers import PydanticOutputParser\n",
    "from pydantic import BaseModel,Field,field_validator\n",
    "from typing import List"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建模型\n",
    "model = OpenAI(openai_api_key = os.environ['OPENAI_API_KEY'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义数据结构\n",
    "class Joke(BaseModel):\n",
    "    setup: str = Field(description=\"question to set up a joke\")\n",
    "    punchline: str = Field(description=\"answer to resolve the joke\")\n",
    "\n",
    "    # 使用validator装饰器来验证数据\n",
    "    @field_validator('setup')\n",
    "    def question_ends_with_question_mark(cls,field):\n",
    "        if field[-1] != \"?\":\n",
    "            raise ValueError(\"Badly formed question!\")\n",
    "        return field"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\n{\"setup\": \"Why did the chicken cross the road?\", \"punchline\": \"To get to the other side.\"}'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 创建一个用于提示LLM生成数据结构的查询\n",
    "joke_query = \"Tell me a joke.\"\n",
    "# 设置一个输出解析器，并将指令注入提示词模板\n",
    "parser = PydanticOutputParser(pydantic_object=Joke)\n",
    "\n",
    "prompt = PromptTemplate(\n",
    "    template=\"Answer the user query\\n{format_instructions}\\n{query}\\n\",\n",
    "    input_variables=['query'],\n",
    "    partial_variables={'format_instructions':parser.get_format_instructions()}\n",
    ")\n",
    "\n",
    "_input = prompt.format_prompt(query=joke_query)\n",
    "output = model(_input.to_string())\n",
    "\n",
    "output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['{\"setup\": \"Why was the math book sad?\"',\n",
       " '\"punchline\": \"Because it had too many problems.\"}']"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将用户输入\"ice cream flavors\"绑定到提示词模板的query变量上\n",
    "_input = prompt.format(subject = \"ice cream flavors\",query=joke_query)\n",
    "\n",
    "output = model(_input)\n",
    "\n",
    "output_parser.parse(output)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 结构化输出解析器(StructuredOutputParser)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入结构化输出解析器和响应提要\n",
    "from langchain.output_parsers import StructuredOutputParser,ResponseSchema\n",
    "# 导入提示词模板\n",
    "from langchain.prompts import (\n",
    "    PromptTemplate,\n",
    "    ChatPromptTemplate,\n",
    "    HumanMessagePromptTemplate\n",
    ")\n",
    "# 导入LLM\n",
    "from langchain.llms import OpenAI\n",
    "# 导入Chat模型\n",
    "from langchain.chat_models import ChatOpenAI\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义响应模式\n",
    "response_schema = [\n",
    "    ResponseSchema(\n",
    "        name = \"answer\",\n",
    "        description = \"answer to the user's question\",\n",
    "    ),\n",
    "    ResponseSchema(\n",
    "        name = \"source\",\n",
    "        description = (\n",
    "            \"source used to answer the user's question,\"\n",
    "            \"should be a website.\"\n",
    "        )\n",
    "    )\n",
    "]\n",
    "\n",
    "output_parser  =  StructuredOutputParser.from_response_schemas(response_schema)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'answer': 'Paris', 'source': 'https://www.britannica.com/place/Paris'}"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 创建格式化的指令\n",
    "format_instructions = output_parser.get_format_instructions()\n",
    "\n",
    "# 创建提示词模板\n",
    "prompt = PromptTemplate(\n",
    "    template = (\n",
    "        \"answer the users question as best as possible.\\n\"\n",
    "        \"{format_instructions}\\n{question}\"\n",
    "    ),\n",
    "    input_variables = ['question'],\n",
    "    partial_variables= {\n",
    "        \"format_instructions\": format_instructions\n",
    "    }\n",
    ")\n",
    "\n",
    "# 创建LLM\n",
    "model = OpenAI(\n",
    "    openai_api_key = os.environ['OPENAI_API_KEY']\n",
    ")\n",
    "\n",
    "_input = prompt.format_prompt(question=\"What is the capital of France?\")\n",
    "output = model(_input.to_string())\n",
    "\n",
    "output_parser.parse(output)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'answer': 'Paris', 'source': 'https://en.wikipedia.org/wiki/Paris'}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 创建一个聊天模型包装器中使用这个方法的实例\n",
    "chat_model = ChatOpenAI(\n",
    "    openai_api_key = os.environ['OPENAI_API_KEY'],\n",
    ")\n",
    "\n",
    "\n",
    "# 创建聊天提示词模板类\n",
    "prompt = ChatPromptTemplate(\n",
    "    messages = [\n",
    "        HumanMessagePromptTemplate.from_template(\n",
    "            \"answer the users question as best as possible.\\n\"\n",
    "            \"{format_instructions}\\n{question}\"\n",
    "        )\n",
    "    ],\n",
    "    input_variables = ['question'],\n",
    "    partial_variables = {\n",
    "        \"format_instructions\": format_instructions\n",
    "    }\n",
    ")\n",
    "\n",
    "_input = prompt.format_prompt(question=\"What is the capital of France?\")\n",
    "output = chat_model(_input.to_messages()) # 将输出转换为Messag类（因为我们的提示词模板是MessageTemplate类的实例）\n",
    "\n",
    "output_parser.parse(output.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
